//+FHEADER /////////////////////////////////////////////////////////////////////////////
// FILE NAME :  afifo_in.v
// DEPARTMENT:  N/A
// AUTHOR:DingXU
// AUTHOR'S EMAIL : N/A
// --------------------------------------------------------------------------
// RELEASE HISTORY :
// VERSION DATE AUTHORDESCRIPTION 
// 1.0 2020-05-17  DingXU initial version  
// Added comments in header:
//---------------------------------------------------------------------------
// KEYWORDS :  afifo
//--------------------------------------------------------------------------
// PURPOSE:   afifo
// --------------------------------------------------------------------------
// PARAM NAME RANGE:  N/A
// -----------------------------------------------------------------------
// REUSE ISSUES:
// Reset Strategy : Asynchronous, active low system level reset : 
// Clock Domains : i_r_clk, i_w_clk,
// Critical TiminG: N/A
// Instantiations : no
// Synthesizable : Yes 
//-FHEADER //////////////////////////////////////////////////////////////////////
module afifo_in(
                   i_rst_n         ,//async reset
                   i_r_clk         ,
                   i_w_clk         ,
                   i_r_en          ,
                   i_w_en          ,
                   i_w_data        ,
            
                   o_r_data        ,
                   o_full          , //Full flag
                   o_empty           //Empty flag
                   );
            
///////////////////////////////////////
//declaration of input & output
///////////////////////////////////////           
input              i_rst_n         ;
input              i_r_clk         ;
input              i_w_clk         ;
input              i_r_en          ;
input              i_w_en          ;
input    [0:95]    i_w_data        ;
            
output   [0:95]    o_r_data        ;
output             o_full          ;
output             o_empty         ;

///////////////////////////////////////
//declaration of registers & wires
///////////////////////////////////////
reg                o_full          ; 
reg                o_empty         ; 
reg      [6:0]     w_addrgray      ;
reg      [6:0]     w_nextgray      ;
reg      [6:0]     r_addrgray      ;
reg      [6:0]     r_nextgray      ;
reg      [6:0]     r_lastgray      ;
reg      [6:0]     w_addr          ;
reg      [6:0]     r_addr          ;
reg                emptyg          ;
reg                fullg           ;
reg                almostemptyg    ;
reg                almostfullg     ;
wire               r_allow         ;
wire               w_allow         ;

///////////////////////////////////////
//            program 
///////////////////////////////////////

///////////////////////////////////////
//DP RAM instantiation for FIFO . Module is 128 *96, 
//of which one address location is sacrificed for the
//overall speed of design
///////////////////////////////////////

dpram u0(
       .i_r_clk(i_r_clk)   ,
       .i_w_clk(i_w_clk)   ,
       .i_r_en(r_allow)    ,
       .i_w_en(w_allow)    , 
       .i_r_addr(r_addr)   ,
       .i_w_addr(w_addr)   ,
       .i_w_data(i_w_data) ,
       .o_r_data(o_r_data)      
       );

///////////////////////////////////////
//Allow flags fetermine whether FIFO control logic can * 
//operate. If i_r_enis driven high ,and the FIFO if *
//not Empty , then Reads are allows . Similarly, if the *
//i_w_en signal is high ,and the FIFO is not Full, *
//then Writes are allowed *
///////////////////////////////////////

assign r_allow = ((i_r_en == 1'b1)&& (o_empty == 1'b0)); 
assign w_allow = ((i_w_en == 1'b1));

///////////////////////////////////////
//Full flag is set on i_rst_n (initial, but it is cleared
//on the first valid Write clock edge after Fifo rst is 
//de-asserted), or when Gray-code counters are one away 
//from being equal (the Write Gray-code address is equal
//to the Last Read Gray-code address), or when the Next 
//Write Gray-code address is equal to the Last Read Gray-code 
//address, and a Write operation is about to be performed.
///////////////////////////////////////

always@(posedge i_w_clk or negedge i_rst_n)
if(i_rst_n == 1'b0)
    o_full <= 1'b1;
else
    o_full <= (fullg || (almostfullg && i_w_en && (o_full == 1'b0))); 

///////////////////////////////////////
//Empty flag is set on i_rst_n (initial), or when gray 
//code counters are equal, or when there is one word in 
//the FIFO. and a Read operation is about to be performed
/////////////////////////////////////// 
    
always@(posedge i_r_clk or negedge i_rst_n)
if(i_rst_n == 1'b0)
    o_empty <= 1'b1;
else
    o_empty <= (emptyg || (almostemptyg && i_r_en && (o_empty == 1'b0))); 
    
///////////////////////////////////////
//Generation of Read address pointers. The primary one is 
//binary (r_addr), and the Gray-code derivatives are 
//generated via pipelining the binary-to-Gray-code result 
//The initial values are important, so they're in sequence.
//Grey-code addresses are used so that the registered
//Full and Empty flags are always clean, and never in an 
//unknown state due to the asynchronous relationship of the 
//Read and Write clocks. In the worst case scenario, Full 
//and Empty would simply stay active one cycle longer, but 
//it would not generate an error or give false values.
///////////////////////////////////////      
   
always@(posedge i_r_clk or negedge i_rst_n)
if(i_rst_n == 1'b0)
    r_addr <= 7'd0;
else if (r_allow == 1'b1)
    r_addr <= r_addr + 7'd1; 

    
always@(posedge i_r_clk or negedge i_rst_n)
if(i_rst_n == 1'b0)
    r_nextgray <= 7'd0;
else if (r_allow == 1'b1)
    r_nextgray <= {r_addr[6] , (r_addr[6] ^ r_addr[5]) , (r_addr[5] ^ r_addr[4]) , (r_addr[4] ^ r_addr[3]) , (r_addr[3] ^ r_addr[2]) , (r_addr[2] ^ r_addr[1]) , (r_addr[1] ^ r_addr[0])}; 


always@(posedge i_r_clk or negedge i_rst_n)
if(i_rst_n == 1'b0)
    r_addrgray <= 7'b1000001;
else if (r_allow == 1'b1)
    r_addrgray <= r_nextgray;

     
always@(posedge i_r_clk or negedge i_rst_n)
if(i_rst_n == 1'b0)
    r_lastgray <= 7'b1000011;
else if (r_allow == 1'b1)
    r_lastgray <= r_addrgray; 

  
///////////////////////////////////////
//Generation of Write address pointers. Identical copy of* 
//read pointer generation above, except for names.*
///////////////////////////////////////   

always@(posedge i_w_clk or negedge i_rst_n)
if(i_rst_n == 1'b0)
    w_addr <= 7'd0;
else if (w_allow == 1'b1)
    w_addr <= w_addr + 7'd1;

    
always@(posedge i_w_clk or negedge i_rst_n)
if(i_rst_n == 1'b0)
    w_nextgray <= 7'd0;
else if (w_allow == 1'b1)
    w_nextgray <= {w_addr[6] , (w_addr[6] ^ w_addr[5]) , (w_addr[5] ^ w_addr[4]) , (w_addr[4] ^ w_addr[3]) , (w_addr[3] ^ w_addr[2]) , (w_addr[2] ^ w_addr[1]) , (w_addr[1] ^ w_addr[0])}; 


always@(posedge i_w_clk or negedge i_rst_n)
if(i_rst_n == 1'b0)
    w_addrgray <= 7'b1000001;
else if (w_allow == 1'b1)
    w_addrgray <= w_nextgray;

///////////////////////////////////////
//When the Write/Read Gray-code addresses are equal, the 
//FIFO is Empty, and Emptyg (combinatorial) is asserted.
//When the Write Gray-code address is equal to the Next 
//Read Gray-code address (1 word in the FIFO), then the 
//FIFO potentially could be going Empty (if Read enable is 
//asserted, which is used in the logic that generates the 
//registered version of Empty).
//Similarly, when the Write Gray-code address is equal to 
//the Last Read Gray-code address, the FIFO is Full. To.
//have utilized the Full pddress space (128 addresses) 
//would have required extra logic to determine Full/Eempty 
//on equal addresses, and this would have slowed down the 
//overall performance. Lastly, when the Next Write Gray 
//code address is equal to the Last Read Gray-code address 
//the FIFO is Almost Full, with only one word left, and
//it is conditional on Write enable being asserted.
///////////////////////////////////////    
     
always@(w_addrgray or r_addrgray) 
if(w_addrgray == r_addrgray)  
    emptyg <=1'b1;
else
    emptyg <=1'b0; 
 
always@(w_addrgray or r_nextgray) 
if(w_addrgray == r_nextgray)  
    almostemptyg <=1'b1;
else
    almostemptyg <=1'b0;
    
always@(w_addrgray or r_lastgray) 
if(w_addrgray == r_lastgray)  
    fullg <=1'b1;
else
    fullg <=1'b0;
    
always@(w_nextgray or r_lastgray) 
if(w_nextgray == r_lastgray)  
    almostfullg <=1'b1;
else
    almostfullg <=1'b0;

endmodule
     
    